﻿using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using RAP.Framework.Libary.Configuration;
using RAP.Framework.Libary.DataSource;
using RAP.Framework.Libary.DataSource.Context;
using RAP.Framework.Libary.DataSource.Model;
using RAP.Framework.Libary.ServerCacheManager;
using RAP.Framework.Libary.Utils;
using static RAP.Framework.Libary.DataSource.ServiceUtils;

namespace RAP.Framework.Libary.SqlDataService.Service
{
  /// <summary>
  /// SQL数据访问服务抽象基类
  /// </summary>
  public abstract class SqlDataServiceBase : ISqlDataService
  {
    /// <summary>
    /// 是否处于调试状态
    /// </summary>
    public virtual bool IsDebug { get; set; }

    /// <summary>
    /// 缓存对象
    /// </summary>
    public virtual ICacheManager Cache { get; set; }

    /// <summary>
    /// 默认的数据访问对象所在的Controller的NameSpace，用来拼接SqlTemplate的Id
    /// </summary>
    public virtual string NameSpace { get; set; }

    /// <summary>
    /// 默认的数据缓存代理对象的Key
    /// </summary>
    public virtual string CacheProxyKey { get; set; } = "DefaultCacheProxy";

    /// <summary>
    /// 
    /// </summary>
    protected virtual IServiceProvider ServiceProvider { get; set; }

    /// <summary>
    /// 
    /// </summary>
    protected virtual IConfigurationManager ConfigurationManager { get; set; }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="cache"></param>
    /// <param name="configurationManager"></param>
    /// <param name="serviceProvider"></param>
    public SqlDataServiceBase(ICacheManager cache, IConfigurationManager configurationManager, IServiceProvider serviceProvider)
    {
      Cache = cache;
      ServiceProvider = serviceProvider;
      ConfigurationManager = configurationManager;
    }

    #region 事件
    public event Action<SqlDataServiceEventArgs> OnBeginExecute;
    public event Action<SqlDataServiceEventArgs> OnEndExecute;

    protected void FireBeginExecuteEvent(SqlDataServiceEventArgs param)
    {
      OnBeginExecute?.Invoke(param);
    }

    /// <summary>
    /// 触发结束查询的事件
    /// </summary>
    /// <param name="param"></param>
    protected void FireEndExecuteEvent(SqlDataServiceEventArgs eventParam, IQueryResult queryResult)
    {
      if (OnEndExecute != null)
      {
        eventParam.Duration = DateTime.Now.Subtract(eventParam.StartTime).TotalMilliseconds.ToInt32Value();
        eventParam.EndTime = DateTime.Now;
        eventParam.Error = queryResult.Error;
        eventParam.DataCount = queryResult.DataServiceContext.Progress.TotalDataCount;
        OnEndExecute.Invoke(eventParam);
      }
    }
    #endregion 事件

    /// <summary>
    /// 根据IQueryParams对象复制一个新的IQueryParams对象
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public virtual IQueryParams CreateQueryParams(IQueryParams param = null)
    {
      var queryParams = ServiceProvider.GetService<IQueryParams>();
      if (param == null)
      {
        return queryParams;
      }
      return param.CopyTo(queryParams);
    }

    /// <summary>
    /// 根据键值对创建查询参数
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public virtual IQueryParams CreateQueryParams(IDictionary param = null)
    {
      var queryParams = ServiceProvider.GetService<IQueryParams>();
      return queryParams.CopyFrom(param);
    }

    /// <summary>
    /// 复制参数对象，如果参数对象为null，则创建一个新的
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    protected virtual IQueryParams GetParam(IQueryParams param, string templateName, string defaultDataSourceId)
    {
      var apiQueryParams = CreateQueryParams(param);
      if (apiQueryParams.TemplateId.IsNullOrEmpty())
      {
        string tplName = templateName;
        if (templateName.IsNotNullOrEmpty())
        {
          if (templateName.IndexOf(".") < 0)
          {
            tplName = $"{NameSpace}.{templateName}";
          }
        }
        if (tplName.IsNotNullOrEmpty())
        {
          apiQueryParams.TemplateId = tplName;
        }
      }
      apiQueryParams.CacheProxyKey = CacheProxyKey;
      apiQueryParams.AllowEmptySql = true;
      /*
        设置dataSource属性值，如果参数dataSource为空
        ，则从param中获取dataSource值，如果param中为空，则使用配置节点“DataConfig.DefaultDataSourceId”
      */
      apiQueryParams.DataSource = defaultDataSourceId.IsNullValue
      (
        () => apiQueryParams.DataSource
      );
      return apiQueryParams;
    }

    protected virtual SqlDataServiceEventArgs CreateEventArgs(IQueryParams param)
    {
      return new SqlDataServiceEventArgs
      {
        Param = param,
        ServiceId = SERVICE_SQL_IDENTITY,
        StartTime = DateTime.Now
      };
    }

    /// <summary>
    /// 异步调用指定DataService服务对象
    /// </summary>
    /// <param name="param">查询参数</param>
    /// <param name="allowFireEvent">是否触发事件</param>
    /// <param name="serviceIdentity">服务类别Identity</param>
    /// <param name="method">调用服务的命令</param>
    /// <returns></returns>
    protected virtual async Task<IQueryResult> ExecuteDataService(
      IQueryParams param
      , bool allowFireEvent = true
      , IDataServiceContext context = null
       )
    {
      SqlDataServiceEventArgs eventParam = null;
      //触发OnBeginExecute事件
      if (allowFireEvent)
      {
        eventParam = CreateEventArgs(param);
        FireBeginExecuteEvent(eventParam);
      }
      var queryResult = await DataSourceHelper.InvokeDataService
      (
        SERVICE_SQL_IDENTITY
        , param
        , context
      );
      context = queryResult.DataServiceContext;
      if (context.CachedResult.IsNotNullOrEmpty())
      {
        context.CachedResult.CopyTo(queryResult.Data as IDictionary);
      }
      //触发OnEndExecute事件
      if (allowFireEvent)
      {
        FireEndExecuteEvent(eventParam, queryResult);
      }
      try
      {
        return queryResult;
      }
      finally
      {
        if (context.Disposable)
        {
          context.Dispose();
        }
        context = null;
        eventParam = null;
        queryResult = null;
      }
    }

    /// <summary>
    /// 调用指定DataService服务对象
    /// </summary>
    /// <param name="queryParams">查询参数</param>
    /// <param name="allowFireEvent">是否触发事件</param>
    /// <param name="serviceIdentity">服务类别Identity</param>
    /// <param name="method">调用服务的命令</param>
    /// <returns></returns>
    protected virtual IQueryResult ExecuteDataServiceSync(IQueryParams param
      , bool allowFireEvent = true
      , IDataServiceContext context = null
       )
    {
      SqlDataServiceEventArgs eventParam = null;
      //触发OnBeginExecute事件
      if (allowFireEvent)
      {
        eventParam = CreateEventArgs(param);
        FireBeginExecuteEvent(eventParam);
      }
      var queryResult = DataSourceHelper.InvokeDataServiceSync
      (
        SERVICE_SQL_IDENTITY
        , param
        , context
      );
      context = queryResult.DataServiceContext;
      var cacheResult = queryResult.DataServiceContext.CachedResult;
      if (cacheResult.IsNotNullOrEmpty())
      {
        cacheResult.CopyTo(queryResult.Data as IDictionary);
      }
      //触发OnEndExecute事件
      if (allowFireEvent)
      {
        FireEndExecuteEvent(eventParam, queryResult);
      }
      try
      {
        return queryResult;
      }
      finally
      {
        if (context.Disposable)
        {
          context.Dispose();
        }
        context = null;
        eventParam = null;
        queryResult = null;
      }
    }

    /// <summary>
    /// 同步执行查询
    /// </summary>
    /// <param name="param"></param>
    /// <param name="cacheKey"></param>
    /// <param name="actionName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual IQueryResult ExecuteSync(
      IQueryParams param = null,
      string cacheKey = CacheTypes.None,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      var queryParams = GetParam(param, templateName, defaultDataSourceId);
      IQueryResult result = null;
      if (cacheKey.IsNotNullOrEmpty()
          && cacheKey.IsNotEqual(CacheTypes.None))
      {
        //cacheKey不为空且不等于ServiceUtils.CacheTypes.None
        if (cacheKey == CacheTypes.Auto)
        {
          //自动构造cacheKey
          cacheKey = CacheKey.Create(CacheProxyKey, NameSpace, templateName).ToString();
        }
        queryParams.CacheProxyKey = cacheKey;
        if (Cache.Contains(cacheKey))
        {
          result = new QueryResult
          {
            Data = Cache.Get(cacheKey)
          };
        }
        else
        {
          result = ExecuteDataServiceSync(queryParams, allowFireEvent, context);
          if (!result.HasError)
          {
            Cache.SetAsync(cacheKey, result.Data);
          }
        }
      }
      else
      {
        result = ExecuteDataServiceSync(queryParams, allowFireEvent, context);
      }
      return result;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="param"></param>
    /// <param name="cacheKey"></param>
    /// <param name="templateName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual IQueryResult ExecuteSync(
      IDictionary param,
      string cacheKey = CacheTypes.None,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      var queryParam = CreateQueryParams(param);
      return ExecuteSync(queryParam, cacheKey, templateName, defaultDataSourceId, allowFireEvent, context);
    }

    /// <summary>
    /// 查询数据，默认有先从缓存获取数据
    /// </summary>
    /// <param name="param">查询参数</param>
    /// <param name="cacheKey">缓存的方式</param>
    /// <param name="actionName">模板Id</param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual async Task<IQueryResult> Execute(
      IQueryParams param = null,
      string cacheKey = CacheTypes.Auto,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      var queryParams = GetParam(param, templateName, defaultDataSourceId);
      //异步执行数据库查询，不阻塞当前Http线程
      IQueryResult result = null;
      if (cacheKey.IsNotNullOrEmpty()
          && cacheKey.IsNotEqual(CacheTypes.None))
      {
        //cacheKey不为空且不等于ServiceUtils.CacheTypes.None
        if (cacheKey == CacheTypes.Auto)
        {
          //自动构造cacheKey
          cacheKey = CacheKey.Create(CacheProxyKey, NameSpace, templateName).ToString();
        }
        queryParams.CacheProxyKey = cacheKey;
        if (Cache.Contains(cacheKey))
        {
          result = new QueryResult
          {
            Data = Cache.Get(cacheKey)
          };
        }
        else
        {
          result = await ExecuteDataService(queryParams, allowFireEvent, context);
          if (!result.HasError)
          {
            Cache.SetAsync(cacheKey, result.Data);
          }
        }
      }
      else
      {
        result = await ExecuteDataService(queryParams, allowFireEvent, context);
      }
      return result;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="param"></param>
    /// <param name="cacheKey"></param>
    /// <param name="templateName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual async Task<IQueryResult> Execute(
      IDictionary param,
      string cacheKey = CacheTypes.Auto,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      var queryParam = CreateQueryParams(param);
      return await Execute(queryParam, cacheKey, templateName, defaultDataSourceId, allowFireEvent, context);
    }

    /// <summary>
    /// 异步执行查询，获取数据库受影响的行数
    /// </summary>
    /// <param name="param"></param>
    /// <param name="cacheKey"></param>
    /// <param name="templateName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual async Task<object> ExecuteNonQuery(
      IQueryParams param = null,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      param.DataType = DataResultTypes.AffectedRowCount;
      var result = await Execute(param, CacheTypes.None, templateName, defaultDataSourceId, allowFireEvent, context);
      return (result.Data as IDictionary).GetValue("0", result);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="param"></param>
    /// <param name="templateName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual async Task<object> ExecuteNonQuery(
      IDictionary param,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      var queryParam = CreateQueryParams(param);
      return await ExecuteNonQuery(queryParam, templateName, defaultDataSourceId, allowFireEvent, context);
    }

    /// <summary>
    /// 执行查询，不访问缓存
    /// </summary>
    /// <param name="param"></param>
    /// <param name="templateName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual async Task<IQueryResult> ExecuteNoCache(
      IQueryParams param = null,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      return await Execute(param, CacheTypes.None, templateName, defaultDataSourceId, allowFireEvent, context);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="param"></param>
    /// <param name="templateName"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="allowFireEvent"></param>
    /// <returns></returns>
    public virtual async Task<IQueryResult> ExecuteNoCache(
      IDictionary param,
      [CallerMemberName] string templateName = null,
      string defaultDataSourceId = null,
      bool allowFireEvent = true,
      IDataServiceContext context = null)
    {
      var queryParam = CreateQueryParams(param);
      return await Execute(queryParam, CacheTypes.None, templateName, defaultDataSourceId, allowFireEvent, context);
    }
  }
}
